using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using TDS = Tridion.ContentManager.Interop.TDS;
using COM = System.Runtime.InteropServices.ComTypes;
using TDSDefines = Tridion.ContentManager.Interop.TDSDefines;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;


namespace Tridion.Gateway
{
    public class ContextManager : TDS.TDSE, IContextManager
    {

        String user;
        TDS.TDSE proxy;
        #region AppDomain   
        //AppDomain appDomain = null;
        #endregion

        private ContextManager(String User)
        {
            user = User;
        }

        private void Init()
        {
            if (proxy == null)
            {
                proxy = new TDS.TDSE();
                if (!String.IsNullOrEmpty(user))
                    proxy.Impersonate(user);
            }

            #region AppDomain
            //Type type = typeof(TDS.TDSEClass);
            //AppDomainSetup info = new AppDomainSetup();
            //info.ApplicationBase = AppDomain.CurrentDomain.RelativeSearchPath;
            //info.DisallowApplicationBaseProbing = false;
            //info.ShadowCopyFiles = "false"; //boolean as string? weird.
            //string[] configFiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.config");
            //if (configFiles.Length == 1)
            //{
            //    info.ConfigurationFile = configFiles[0];
            //}
            //appDomain = AppDomain.CreateDomain("Context", null, info);
            ///// proxy runs *inside* new app domain;
            //proxy = (TDS.TDSE)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
            #endregion

        }

        private void UnInit()
        {
            if (proxy != null)
            {
                Marshal.ReleaseComObject(proxy);
                proxy = null;
            }

            #region AppDomain
           //if (appDomain != null && !appDomain.IsFinalizingForUnload())
           //{
           //    AppDomain.Unload(appDomain);
           //    appDomain = null;
           //}
           #endregion
        }

        private object Invoke(object[] args)
        {
            Init();
            List<Type> types = new List<Type>();
            MethodBase method = (new StackFrame(1)).GetMethod();
            ParameterInfo[] parameters = method.GetParameters();
            
            Array.ForEach<ParameterInfo>(parameters, new Action<ParameterInfo>(delegate(ParameterInfo parameter) {
                types.Add(parameter.ParameterType); 
            
            }));
            try
            {
                return proxy.GetType().GetMethod(method.Name, types.ToArray()).Invoke(proxy, args);
            }
            catch (System.Reflection.TargetInvocationException e)
            {
                if (e.InnerException is System.Runtime.InteropServices.COMException)
                {
                    if (((System.Runtime.InteropServices.COMException)e.InnerException).ErrorCode == -2147023174)
                    {
                        UnInit();
                    }
                }
                throw e.InnerException;
            }
 
        }

        public static IContextManager CreateContext(String user)
        {
            ContextManager manager = new ContextManager(user);
            manager.Init();
            return manager as IContextManager; //TDS.TDSE;
        }
        
        #region _TDSE Members

        public void _VtblGap7_3()
        { 
                   
        }

        public object CreateEmptyCollection(TDSDefines.ItemType itemType)
        {
            object[] args = new object[] { itemType };
            return Invoke(args) as TDS.ListRowFilter;
        }

        public TDS.ListRowFilter CreateListRowFilter()
        {
            object[] args = new object[] {};
            return Invoke(args) as TDS.ListRowFilter;
        }

        public void FlushCache(object ObjectOrURI)
        {
            object[] args = new object[] { ObjectOrURI };
            Invoke(args);
        }

        public TDS.Component GetComponent(string ComponentID, object Publication, bool EditMode, int Version)
        {
            object[] args = new object[] { ComponentID, Publication, EditMode, Version };
            return Invoke(args) as TDS.Component;
        }

        public TDS.ComponentTemplate GetComponentTemplate(string ComponentTemplateID, object Publication, bool EditMode, int Version)
        {
            object[] args = new object[] { ComponentTemplateID, Publication, EditMode, Version };
            return Invoke(args) as TDS.ComponentTemplate;
        }

        public TDS.Schemas GetDestinationProtocols()
        {
            object[] args = new object[] { };
            return Invoke(args) as TDS.Schemas;
        }

        public string GetEnumerationValues(string enumerations)
        {
            object[] args = new object[] { enumerations };
            return Invoke(args) as string;
        }

        public TDS.Folder GetFolder(string folderID, object Publication)
        {
            object[] args = new object[] { folderID, Publication };
            return Invoke(args) as TDS.Folder;
        }

        public TDS.Groups GetGroups()
        {
            object[] args = new object[] { };
            return Invoke(args) as TDS.Groups;
        }

        public string GetListApprovalStatuses(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args) as string;
        }

        public string GetListDestinationProtocols(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args) as string;
        }

        public string GetListDirectoryServiceTestUsers(string directoryService, string searchDN, TDSDefines.EnumDirectorySearchTypes searchType)
        {
            object[] args = new object[] { directoryService, searchDN, searchType };
            return Invoke(args) as string;
        }

        public string GetListDirectoryServices(TDS.ListRowFilter rowFilter)
        {
            object[] args = new object[] { rowFilter };
            return Invoke(args) as string;
        }

        public string GetListItems(TDSDefines.ListColumnFilter columnFilter, TDS.ListRowFilter rowFilter)
        {
            object[] args = new object[] { columnFilter, rowFilter };
            return Invoke(args) as string;
        }

        public string GetListMultimediaTypes(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args) as string;
        }

        public string GetListNonSystemUsers(TDSDefines.EnumNonSystemUserType userType, string nameOfDomainOrDirectory)
        {
            object[] args = new object[] { userType, nameOfDomainOrDirectory };
            return Invoke(args)as string;
        }

        public string GetListPublicationTargets(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args)as string;
        }

        public string GetListPublications(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args) as string;
        }

        public string GetListTargetTypes(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args) as string;
        }

        public string GetListTemplateTypes(TDSDefines.ListColumnFilter columnFilter, TDS.ListRowFilter rowFilter)
        {
            object[] args = new object[] { columnFilter, rowFilter };
            return Invoke(args) as string;
        }

        public string GetListTrustees(TDSDefines.TrusteeType trusteeTypes, TDSDefines.ListColumnFilter columnFilter, TDS.ListRowFilter rowFilter)
        {
            object[] args = new object[] { trusteeTypes, columnFilter, rowFilter };
            return Invoke(args) as string;
        }

        public string GetListVirtualFolderTypes(TDSDefines.ListColumnFilter columnFilter)
        {
            object[] args = new object[] { columnFilter };
            return Invoke(args) as string;
        }

        public string GetLocalizedErrorXML(string errorXML, int LangID)
        {
            object[] args = new object[] { errorXML, LangID };
            return Invoke(args) as string;
        }

        public TDS.Logging GetLogging()
        {
            object[] args = new object[] {  };
            return Invoke(args) as TDS.Logging;
        }

        public TDS.ManagementInfo GetManagementInfo()
        {
            object[] args = new object[] {  };
            return Invoke(args) as TDS.ManagementInfo;
        }

        public TDS.MultimediaTypes GetMultimediaTypes()
        {
            object[] args = new object[] {  };
            return Invoke(args) as TDS.MultimediaTypes;
        }

        public object GetNewObject(TDSDefines.ItemType itemType, object inOrgItem, object contextPublication)
        {
            object[] args = new object[] { itemType, inOrgItem, contextPublication };
            return Invoke(args);
        }

        public object GetObject(string URI, TDSDefines.EnumOpenMode mode, object contextPublication, TDSDefines.XMLReadFilter filter)
        {
            object[] args = new object[] { URI, mode, contextPublication, filter };
            return Invoke(args);
        }

        public TDS.Page GetPage(string pageID, object Publication, bool EditMode, int Version)
        {
            object[] args = new object[] { Publication, EditMode, Version };
            return Invoke(args) as TDS.Page;
        }

        public TDS.PageTemplate GetPageTemplate(string pageTemplateID, object Publication, bool EditMode, int Version)
        {
            object[] args = new object[] { pageTemplateID, Publication, Version };
            return Invoke(args) as TDS.PageTemplate;
        }

        public TDS.Publication GetPublication(string publicationID)
        {
            object[] args = new object[] { publicationID };
            return Invoke(args) as TDS.Publication;
        }

        public TDS.PublicationTarget GetPublicationTarget(string pubTargetID)
        {
            object[] args = new object[] { pubTargetID };
            return Invoke(args) as TDS.PublicationTarget;
        }

        public TDS.PublicationTargets GetPublicationTargets()
        {
            object[] args = new object[] {  };
            return Invoke(args) as TDS.PublicationTargets;
        }

        public TDS.Publications GetPublications()
        {
            object[] args = new object[] {  };
            return Invoke(args) as TDS.Publications; 
        }

        public TDS.PublishTransaction GetPublishTransaction(string publishTransactionID)
        {
            object[] args = new object[] { publishTransactionID };
            return Invoke(args) as TDS.PublishTransaction;
        }

        public TDS.Search GetSearch()
        {
            object[] args = new object[] { };
            return Invoke(args) as TDS.Search;
        }

        public TDS.StructureGroup GetStructureGroup(string structureGroupID, object Publication)
        {
            object[] args = new object[] { structureGroupID, Publication };
            return Invoke(args) as TDS.StructureGroup;
        }

        public string GetSystemXSD(string nameOrNamespaceURI)
        {
            object[] args = new object[] { nameOrNamespaceURI };
            return Invoke(args) as string;

        }

        public TDS.TargetType GetTargetType(string targetTypeID)
        {
            object[] args = new object[] { targetTypeID };
            return Invoke(args) as TDS.TargetType;
        }

        public TDS.TargetTypes GetTargetTypes()
        {
            object[] args = new object[] { };
            return Invoke(args) as TDS.TargetTypes;
        }

        public TDS.Users GetUsers()
        {
            object[] args = new object[] {  };
            return Invoke(args) as TDS.Users;
        }

        public TDS.Schemas GetVirtualFolderTypes()
        {
            object[] args = new object[] { };
            return Invoke(args) as TDS.Schemas;
        }

        public TDS.WFE GetWFE()
        {
            object[] args = new object[] {};
            return Invoke(args) as TDS.WFE;
        }

        public void Impersonate(string Identity)
        {
            object[] args = new object[] { Identity };
            Invoke(args);
        }

        public void Initialize()
        {
            object[] args = new object[] {};
            Invoke(args);
        }

        public void SetListApprovalStatuses(string newListApprovalStatuses)
        {
            object[] args = new object[] { newListApprovalStatuses };
            Invoke(args);
        }

        public TDS.User User
        {
            get 
            {
                try
                {

                    return proxy.User;
                }
                catch
                {
                    Init();
                    return proxy.User;
                }
                finally
                {

                }
            
            }
        }

        public string Version
        {
            get 
            {
                
                try
                {
                    return proxy.Version;
                }
                catch
                {
                    Init();
                    return proxy.Version;
                }
                finally
                {

                }
            
            }
        }

        #endregion

        public String GetXML(string Uri, TDSDefines.XMLReadFilter Filter)
        {
            string retval = String.Empty;
            object o = proxy.GetObject(Uri, TDSDefines.EnumOpenMode.OpenModeViewOrEdit, null, Filter);
            IntPtr u = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(o);
            if (u != IntPtr.Zero)
            {
                IDispatch iDispatch = (IDispatch)System.Runtime.InteropServices.Marshal.GetTypedObjectForIUnknown(u, typeof(IDispatch));
                if (iDispatch != null)
                {
                    int lcid = System.Globalization.CultureInfo.CurrentCulture.LCID;
                    string[] rgsNames = { "GetXML" };
                    int[] rgDispId = new int[1];
                    Guid riid = Guid.Empty;

                    int result = iDispatch.GetIDsOfNames(ref riid, rgsNames, 1, lcid, rgDispId);

                    if (result == 0)
                    {
                        uint puArgErr;
                        COM.EXCEPINFO pExcepInfo;
                        Type type = Facade.GetType(o);
                        COM.DISPPARAMS pDispParams = new COM.DISPPARAMS();

                        VARIANT v;
                        v.variantType = 22;
                        v.intValue = (int)Filter;


                        IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(VARIANT)));
                        if (ptr != IntPtr.Zero)
                        {
                            object xml;
                            Marshal.StructureToPtr(v, ptr, false);
                            pDispParams.cArgs = 1;
                            pDispParams.rgvarg = ptr;
                            try
                            {
                                result = iDispatch.Invoke(rgDispId[0], ref riid, lcid, COM.INVOKEKIND.INVOKE_FUNC, ref pDispParams, out xml, out pExcepInfo, out puArgErr);
                                if (result == 0)
                                {
                                    retval = xml.ToString();
                                }
                            }
                            catch
                            {
                                throw;
                            }
                            finally
                            {
                                Marshal.FreeCoTaskMem(ptr);
                                Marshal.ReleaseComObject(iDispatch);
                                Marshal.Release(u);
                            }

                        }

                        Marshal.ReleaseComObject(iDispatch);
                    }

                    Marshal.Release(u);
                }
            }

            return retval;
        }

    }
}
